package com.hejing.反射的应用;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * ClassName: Reflectest
 * Package: com.hejing.反射的应用
 * Description:
 *
 * @Author: HeJing
 * @Create: 2023-06-04 20:06
 * @Version: 1.0
 */
public class Reflectest {

    // ************************************ 1、调用指定的属性 *******************************************

    /**
     * 反射的应用3.1: 调用指定的属性
     *
     */
    // 针对于 public int age = 1;
    @Test
    public void test01() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        Class clazz = Class.forName("com.hejing.反射的应用.Person");
        Person per = (Person) clazz.newInstance();

        // 1、获取运行时类中指定名的属性
        Field ageField = clazz.getField("age");

        // 2、获取或者设置此属性的值。
        ageField.set(per, 2);
        System.out.println(ageField.get(per));  // 2
    }

    // 针对于: private String name;
    @Test
    public void test02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        Class clazz = Class.forName("com.hejing.反射的应用.Person");
        Person per = (Person) clazz.newInstance();

        // 1、通过Class实例调用getDeclaredField(String fieldName), 获取运行时类中指定名的属性
        Field nameField = clazz.getDeclaredField("name");

        // 2、setAccessible(true): 确保此属性是可以访问的。
        nameField.setAccessible(true);

        // 3、通过Field类的实例调用get(Object obj) 或 set(Object obj, Object value) 来获取或者设置此属性的值。
        nameField.set(per, "Tom");
        System.out.println(nameField.get(per));  // 2
    }

    // 针对静态属性，private static String info;
    @Test
    public void test03() throws Exception {
        Class clazz = Person.class;

        // 1、通过Class实例调用getDeclaredField(String fieldName), 获取运行时类中指定名的属性
        Field infoField = clazz.getDeclaredField("info");

        // 2、setAccessible(true): 确保此属性是可以访问的。
        infoField.setAccessible(true);

        // 3、通过Field类的实例调用get(Object obj) 或 set(Object obj, Object value) 来获取或者设置此属性的值。
        // infoField.set(clazz, "Tom");
        // System.out.println(infoField.get(Person.class));  // Tom

        // 也可以写成 null (仅限于类变量可以用如下的方式进行设置获取属性值)
        infoField.set(null, "Tom");
        System.out.println(infoField.get(null));  // Tom
    }

    // ************************************ 2、调用指定的方法 *******************************************



    /**
     * 反射的应用3.2: 调用指定的方法
     */
    // 针对于private String showNation(String nation, int age)
    @Test
    public void test04() throws Exception {
        Class clazz = Class.forName("com.hejing.反射的应用.Person");

        Person per = (Person) clazz.newInstance();

        // 1、通过Class的实例调用getDeclaredMethod(String methodName,相应methodName方法的形参)获取指定的方法
        Method showNationMethod = clazz.getDeclaredMethod("showNation", String.class, int.class);

        // 2、setAccessible(true): 确保此方法是可以访问的
        showNationMethod.setAccessible(true);

        // 3、通过Method实例调用invoke(Object obj, Object.. objs)，即为对Method对应的方法的调用
        // invoke()方法的返回值即为Method对应的方法的返回值。
        // 特别的，如果Method对应的方法的返回值类型是void, 则invoke()方法的返回值为 null
        Object returnValue = showNationMethod.invoke(per, "CHN", 10);
        System.out.println(returnValue);  // 我的国籍是：CHN，生活了10年
    }

    // 调用静态的方法: public static void showInfo()
    @Test
    public void test05() throws Exception {
        Class clazz = Class.forName("com.hejing.反射的应用.Person");


        // 1、通过Class的实例调用getDeclaredMethod(String methodName,相应methodName方法的形参)获取指定的方法
        Method showInfoMethod = clazz.getDeclaredMethod("showInfo");

        // 2、setAccessible(true): 确保此方法是可以访问的
        showInfoMethod.setAccessible(true);

        // 3、通过Method实例调用invoke(Object obj, Object.. objs)，即为对Method对应的方法的调用
        // invoke()方法的返回值即为Method对应的方法的返回值。
        // 特别的，如果Method对应的方法的返回值类型是void, 则invoke()方法的返回值为 null
        Object returnValue = showInfoMethod.invoke(null);
        System.out.println(returnValue);  // null
    }

    // ************************************ 3、调用指定的方法 *******************************************

    /**
     * 反射的应用3.3: 调用指定的构造器
     */
    // 针对于 private Person(String name, int age)
    @Test
    public void test06() throws Exception {
        Class clazz = Person.class;
        // 1、通过Class的实例调用getDeclaredConstructor(Class...args) 获取指定参数类型的构造器
        Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);

        // 2、setAccessible(true): 确保此构造器是可以访问的
        constructor.setAccessible(true);

        // 3、通过Constructor实例调用newInstance(Object...objs)，返回一个运行时类的实例。
        Person per = (Person) constructor.newInstance("Tom", 12);
        System.out.println(per);

    }
    
    // 使用Constructor替换原有的使用Class调用newInstance()的方式创建对象
    // 空参构造器
    @Test
    public void test07() throws Exception {
        Class clazz = Person.class;

        // 1、通过Class的实例调用getDeclaredConstructor(Class...args) 获取指定参数类型的构造器
        Constructor constructor = clazz.getDeclaredConstructor();

        // 2、setAccessible(true): 确保此构造器是可以访问的
        constructor.setAccessible(true);

        // 3、通过Constructor实例调用newInstance(Object...objs)，返回一个运行时类的实例。
        Person per = (Person) constructor.newInstance();
        System.out.println(per);
    }
}























